package org.kirhgoff.mathbot;

import java.awt.Color;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GraphicsConfiguration;
import java.awt.GraphicsEnvironment;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Arrays;
import java.util.List;

import javax.imageio.ImageIO;
import javax.swing.JPanel;

public class WorldPanel extends JPanel {
	private static BufferedImage SPACE_IMAGE;
	static {
		try {
			SPACE_IMAGE = ImageIO.read(WorldPanel.class.getClassLoader().getResource("img/space.jpg"));
		} catch (IOException Err) {
			throw new RuntimeException(Err);
		}
	}
	
	private final World world;
	private BufferedImage bufferImage;
	private Graphics2D bufferGraphics;
	private List<Color> ownerColors = Arrays.asList(new Color []{new Color(106, 74, 60), new Color(255, 64, 64), new Color(64, 255, 64)});
	
	public WorldPanel(World world) {
		this.world = world;
	}

	private void initBuffering() {
		// Set up the graphics context.
		if (bufferImage != null) {
			bufferGraphics.dispose();
			bufferImage.flush();
		}

		GraphicsConfiguration gc = GraphicsEnvironment
				.getLocalGraphicsEnvironment().getDefaultScreenDevice()
				.getDefaultConfiguration();

		bufferImage = gc.createCompatibleImage(getWidth(), getHeight());

		bufferGraphics = bufferImage.createGraphics();

		// Turn on AA/Speed
		bufferGraphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
				RenderingHints.VALUE_ANTIALIAS_ON);
		bufferGraphics.setRenderingHint(RenderingHints.KEY_RENDERING,
				RenderingHints.VALUE_RENDER_SPEED);
		bufferGraphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
				RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
	}

	@Override
	public void paint(Graphics g) {
		if (bufferGraphics == null) {
			initBuffering();
		}
		if (SPACE_IMAGE != null) {
			g.drawImage(SPACE_IMAGE, 0, 0, null);
		}		
		renderWorld(getWidth(), getHeight(),bufferGraphics);
		
		g.drawImage(bufferImage, 0, 0, null);		
	}
	//TODO bullshit
	public void update() {
		//TODO: fix this bullshit - we dont need to create image all the time
		initBuffering();
		repaint();
	}
	// Renders the current state of the game to a graphics object
	//
	// The offset is a number between 0 and 1 that specifies how far we are
	// past this game state, in units of time. As this parameter varies from
	// 0 to 1, the fleets all move in the forward direction. This is used to
	// fake smooth animation.
	//
	// On success, return an image. If something goes wrong, returns null.
	void renderWorld(int width, // Desired image width
			int height, // Desired image height
			Graphics2D g) { // Rendering context
		Font planetFont = new Font("Sans Serif", Font.BOLD, 12);
		Font fleetFont = new Font("Sans serif", Font.BOLD, 18);
		Color textColor = Color.BLACK;

		// Determine the dimensions of the viewport in game coordinates.
		double worldTop = Double.MAX_VALUE;
		double worldLeft = Double.MAX_VALUE;
		double worldRight = Double.MIN_VALUE;
		double worldBottom = Double.MIN_VALUE;
		List<PlanetInfo> planets = world.getAllPlanets();
		for (PlanetInfo p : planets) {
			if (p.x() < worldLeft)
				worldLeft = p.x();
			if (p.x() > worldRight)
				worldRight = p.y();
			if (p.y() > worldBottom)
				worldBottom = p.y();
			if (p.y() < worldTop)
				worldTop = p.y();
		}
		
		double worldWidth = worldRight - worldLeft;
		double worldHeight = worldBottom - worldTop;
		double paddingFactor = 0.1; //TODO use most planet growth rate
		worldLeft -= worldWidth * paddingFactor;
		worldRight += worldWidth * paddingFactor;
		worldTop -= worldHeight * paddingFactor;
		worldBottom += worldHeight * paddingFactor;
		
		
		Point[] planetPos = new Point[planets.size()];
		g.setFont(planetFont);
		FontMetrics fm = g.getFontMetrics(planetFont);
		// Determine the best scaling factor for the sizes of the planets.
		double minSizeFactor = Double.MAX_VALUE;
		for (int i = 0; i < planets.size(); ++i) {
			for (int j = i + 1; j < planets.size(); ++j) {
				PlanetInfo a = planets.get(i);
				PlanetInfo b = planets.get(j);
				double dx = b.x() - a.x();
				double dy = b.y() - a.y();
				double dist = Math.sqrt(dx * dx + dy * dy);
				double sizeFactor = dist / (Math.sqrt(a.growthRate()));
				minSizeFactor = Math.min(sizeFactor, minSizeFactor);
			}
		}
		minSizeFactor *= 1.2;
		// Draw the planets.
		int i = 0;
		for (PlanetInfo p : planets) {
			Point pos = getPlanetPos(p, worldTop, worldLeft, worldRight, worldBottom, width, height);
			planetPos[i++] = pos;
			int x = pos.x;
			int y = pos.y;
			double size = minSizeFactor * inherentRadius(p);
			int r = (int) Math.min(size / (worldRight - worldLeft) * width, size
					/ (worldBottom - worldTop) * height);
			g.setColor(GetColor(p.owner(), ownerColors));
			int cx = x - r / 2;
			int cy = y - r / 2;
			g.fillOval(cx, cy, r, r);
			Color c = g.getColor();
			for (int step = 1; step >= 0; step--) {
				g.setColor(g.getColor().brighter());
				g.drawOval(x - (r - step) / 2, y - (r - step) / 2, r - step, r
						- step);
			}
			g.setColor(c);
			for (int step = 0; step < 3; step++) {
				g.drawOval(x - (r + step) / 2, y - (r + step) / 2, r + step, r
						+ step);
				g.setColor(g.getColor().darker());
			}

			java.awt.geom.Rectangle2D bounds = fm.getStringBounds(
					Integer.toString(p.population()), g);
			x -= bounds.getWidth() / 2;
			y += fm.getAscent() / 2;

			g.setColor(textColor);
			g.drawString(Integer.toString(p.population()), x, y);
		}
		// Draw fleets
		g.setFont(fleetFont);
		fm = g.getFontMetrics(fleetFont);
		List<FleetInfo> fleets = world.getAllFleets ();
		for (FleetInfo f : fleets ) {
			Point sPos = planetPos[f.getSourcePlanet().id()];
			Point dPos = planetPos[f.destinationPlanet().id()];
			double tripProgress = 1.0 - (double) f.getRemainingTurns()
					/ f.totalTripLength();
			if (tripProgress > 0.99 || tripProgress < 0.01) {
				continue;
			}
			double dx = dPos.x - sPos.x;
			double dy = dPos.y - sPos.y;
			double x = sPos.x + dx * tripProgress;
			double y = sPos.y + dy * tripProgress;
			java.awt.geom.Rectangle2D textBounds = fm.getStringBounds(
					Integer.toString(f.size()), g);
			g.setColor(GetColor(f.owner(), ownerColors).darker());
			g.drawString(Integer.toString(f.size()),
					(int) (x - textBounds.getWidth() / 2),
					(int) (y + textBounds.getHeight() / 2));
		}
	}
	
	private double inherentRadius(PlanetInfo p) {
		return Math.sqrt(p.growthRate());
	}

	private Point getPlanetPos(PlanetInfo p, double top, double left, double right,
			double bottom, int width, int height) {
		int x = (int) ((p.x() - left) / (right - left) * width);
		int y = height - (int) ((p.y() - top) / (bottom - top) * height);
		return new Point(x, y);
	}	
	
	private Color GetColor(int player, List<Color> colors) {
		if (player > colors.size()) {
			return Color.PINK;
		} else {
			return colors.get(player);
		}
	}	
}
